package com.kjzy.routes;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
//动态路由加载类

@Component
public class DynamicRouteLoader {

    private final String dataId="gateway-routes.json";//从nacos中读取的配置文件名称（json格式）
    private final String group="DEFAULT_GROUP";//组名

    //NacosConfigManager：用来跟 Nacos 通信，拉取配置。
    private final NacosConfigManager nacosConfigManager;
    //    RouteDefinitionWriter：网关用来写入新的路由定义的组件。
    private final RouteDefinitionWriter definitionWriter;
    //用来记录当前已经加载的所有 路由ID，方便后续删除旧的路由。
    private final Set<String> routeIds=new HashSet<>();//用来记录删除routeId
    //通过 Spring容器注入 把 NacosConfigManager 和 RouteDefinitionWriter 传进来，初始化这个类。
    public DynamicRouteLoader(NacosConfigManager nacosConfigManager, RouteDefinitionWriter definitionWriter) {
        this.nacosConfigManager = nacosConfigManager;
        this.definitionWriter = definitionWriter;
    }
//    @PostConstruct：表示Spring容器创建完这个Bean后，自动执行这个方法。
    @PostConstruct
    public void initRouteConfigListener() throws NacosException {
        // 1.项目启动时，先拉取一次配置，并且添加配置监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        // 2.监听到配置变更，需要去更新路由表
                        updateConfigInfo(configInfo);
                    }
                });
        // 3.第一次读取到配置，也需要更新到路由表
        updateConfigInfo(configInfo);
    }
    public void updateConfigInfo(String configInfo){
        System.out.println("监听的路由配置："+configInfo);
        //1、解析配置信息，转为RouteDefinition
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2、先删除所有旧的路由
        for (String routeId : routeIds) {
            //触发实际的删除操作（Reactor 是惰性的，需要订阅才会执行）
            //表示我们不关心删除操作的结果（没有提供成功/错误的回调）
            definitionWriter.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();

        for (RouteDefinition routeDefinition : routeDefinitions) {
            //3、更新路由表,
            //Mono是 Reactor库中的一种响应式发布者（Reactive Publisher），表示0或1个元素的异步序列。
            //Mono.just() 的作用：创建一个已包含确定值的 Mono 流。
            //响应式流是声明式的，只有调用subscribe()会真正执行保存操作
            definitionWriter.save(Mono.just(routeDefinition)).subscribe();
            //记录路由id，便于下次删除
            routeIds.add(routeDefinition.getId());
        }
    }
}
